"""
DIR backoff given an app module of 'app.name':
    settings.<APP_NAME>_MEDIA_ROOT
    settings.<NAME>_MEDIA_URL
    settings.APP_MEDIA_ROOT + '<app/name>/'
    settings.MEDIA_ROOT + '<app/name>/'
    settings.APP_MEDIA_ROOT + '<name>/'
    settings.MEDIA_ROOT + '<name>/'
    '<app_module_dir>/media/'

URL backoff given an app module of 'app.name':
    settings.<APP_NAME>_MEDIA_URL
    settings.<NAME>_MEDIA_URL
    settings.APP_MEDIA_URL + '<app/name>/'
    settibgs.MEDIA_URL + '<app/name>/'

    if settings.APP_MEDIA_ALLOW_FILE_URL:
        'file://<DIR (above)>'
    else:
        raise ImproperlyConfigured
"""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils._os import safe_join
from django.template import Library
from urllib import pathname2url, basejoin, quote
from os import path
register = Library()

def find_app_dir(app_name):
    """given an app name (from setings.INSTALLED_APPS) return the abspath
    to that app directory"""
    i = app_name.rfind('.')
    if i == -1:
        m, a = app_name, None
    else:
        m, a = app_name[:i], app_name[i+1:]
    try:
        if a is None:
            mod = __import__(m, {}, {}, [])
        else:
            mod = getattr(__import__(m, {}, {}, [a]), a)
        return path.dirname(path.abspath(mod.__file__))
    except ImportError, e:
        raise ImproperlyConfigured, 'ImportError %s: %s' % (app_name, e.args[0])

def find_app_media_dir(app_name):
    ## check settings.<APP_NAME>_MEDIA_DIR
    samd = app_name.replace('.', '_').upper() + '_MEDIA_DIR'
    media_dir = getattr(settings, samd, None)
    if media_dir and path.isdir(media_dir): return path.abspath(media_dir)

    ## check settings.<NAME>_MEDIA_DIR
    samd = app_name.split('.')[-1].upper() + '_MEDIA_DIR'
    media_dir = getattr(settings, samd, None)
    if media_dir and path.isdir(media_dir): return path.abspath(media_dir)

    ## check settings.APP_MEDIA_ROOT + '<app/name>/'
    apprel_dir = app_name.replace('.', '/').lower()

    base = getattr(settings, 'APP_MEDIA_ROOT', None)
    if base:
        media_dir = safe_join(base, apprel_dir)
        if path.isdir(media_dir): return media_dir

    ## check settings.MEDIA_ROOT + '<app/name>/'
    base = getattr(settings, 'MEDIA_ROOT', None)
    if base:
        media_dir = safe_join(base, apprel_dir)
        if path.isdir(media_dir): return media_dir

    if '.' in app_name:
        ## check settings.APP_MEDIA_ROOT + '<appname>/'
        apprel_dir = app_name.split('.')[-1].lower()

        base = getattr(settings, 'APP_MEDIA_ROOT', None)
        if base:
            media_dir = safe_join(base, apprel_dir)
            if path.isdir(media_dir): return media_dir

        ## check settings.MEDIA_ROOT + '<appname>/'
        base = getattr(settings, 'MEDIA_ROOT', None)
        if base:
            media_dir = safe_join(base, apprel_dir)
            if path.isdir(media_dir): return media_dir

    ## check '<app_module_dir>/media/'
    base = find_app_dir(app_name)
    if base:
        media_dir = safe_join(base, 'media')
        if path.isdir(media_dir): return media_dir

    ## no app media directory
    return None

def find_app_media_url(app_name, app_media_dir):
    ## settings.<APP_NAME>_MEDIA_URL
    samu = app_name.replace('.', '_').upper() + '_MEDIA_URL'
    app_url = getattr(settings, samu, None)
    if app_url: return app_url

    ## settings.<NAME>_MEDIA_URL
    if '.' in app_name:
        samu = app_name.split('.')[-1].upper() + '_MEDIA_URL'
        app_url = getattr(settings, samu, None)
        if app_url: return app_url

    apprel_url = app_name.replace('.', '/').lower()

    ## settings.APP_MEDIA_URL + '<app/name>/'
    base = getattr(settings, 'APP_MEDIA_URL', None)
    if base: return basejoin(base, apprel_url)

    ## settibgs.MEDIA_URL + '<app/name>/'
    base = getattr(settings, 'MEDIA_URL', None)
    if base: return basejoin(base, apprel_url)

    if getattr(settings, 'APP_MEDIA_ALLOW_FILE_URL', False):
        ## 'file://<DIR (above)>'
        return pathname2url(app_media_dir)

    raise ImproperlyConfigured, "Could not construct a media url for: " + app_name

def find_app_media_dir_and_url(app_name):
    media_dir = find_app_media_dir(app_name)
    if not media_dir: return None
    media_url = find_app_media_url(app_name, media_dir)
    if not media_url.endswith('/'): media_url += '/'
    return media_dir, media_url

app_media_library = dict((app, find_app_media_dir_and_url(app))
                         for app in settings.INSTALLED_APPS)
app_media_apps = tuple(app for app in settings.INSTALLED_APPS
                       if app_media_library[app])

def _check_app_media_dirs():
    check = {}
    for app in app_media_apps:
        root, url = app_media_library[app]
        if url in check and root != app_media_library[check[url]][0]:
            raise ImproperlyConfigured, (
                "Two apps (%s, %s) have the same app media url (%s) pointing to two different directories (%s, %s)" %
                (repr(app), repr(check[url]), repr(url), repr(root), repr(app_media_library[check[url]][0])))
        check[url] = app

def _build_urlmap():
    seen = {}
    for app in app_media_apps:
        root, url = app_media_library[app]
        if url.startswith('http:') or url.startswith('file:'): continue
        if url.startswith('/'): url = url[1:]
        if url in seen: continue
        seen[url] = root
    mr = getattr(settings, "MEDIA_ROOT", None)
    mu = getattr(settings, "MEDIA_URL", None)
    if mr and mu and mu.startswith('/') and mu[1:] not in seen:
        seen[mu[1:]] = mr
    mr = getattr(settings, "APP_MEDIA_ROOT", None)
    mu = getattr(settings, "APP_MEDIA_URL", None)
    if mr and mu and mu.startswith('/') and mu[1:] not in seen:
        seen[mu[1:]] = mr
    return [ (url, seen[url]) for url in reversed(seen.keys()) ]

_check_app_media_dirs()

app_media_urls = _build_urlmap()

def get_app_media(app_name):
    try:
        media = app_media_library[app_name]
    except KeyError:
        raise KeyError, "No app named: " + app_name
    if media is None:
        raise KeyError, "App does not have app media: " + app_name
    return media

def get_app_media_root(app_name):
    return get_app_media(app_name)[0]

def get_app_media_url(app_name):
    return get_app_media(app_name)[1]

@register.simple_tag
def app_media_prefix(app_name):
    return get_app_media_url(app_name)

@register.simple_tag
def app_media_url(app_name, suffix, mtime=True):
    base_dir, base_url = get_app_media(app_name)
    url = basejoin(base_url, quote(suffix))
    file = safe_join(base_dir, suffix)
    if not path.exists(file):
        ## RED_FLAG: report bad URL on settings config!!!
        pass
    else:
        url += '?m=' + hex(int(path.getmtime(file))).lower()[2:]
    return url
